package com.hertz.appr.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.hertz.appr.dao.IBaseDao;
import com.hertz.appr.oauth.AccessToken;
import com.hertz.appr.oauth.OAuth;
import com.hertz.appr.oauth.OAuthToken;
import com.hertz.appr.pojo.db.Robot;
import com.hertz.appr.util.Configuration;
import com.hertz.appr.util.Util;

public class RobotGang implements IRobotGang,Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = -3322494819610670163L;

	private Logger log = Logger.getLogger(RobotGang.class);
	
	private IBaseDao<Robot, Integer> robotDao;
	private Map<String, ApiLimit> robotLimit;
	/**
	 * Key : provider + "-" + flag
	 */
	private Map<String, List<Robot>> robotMap;
	/**
	 * key:robot.id;
	 */
	private Map<String, OAuth> oauthMap;
	private Integer limitReadCount = Configuration.getIntProperty("api.read.limit.perhour");
	private Integer limitWriteCount = Configuration.getIntProperty("api.write.limit.perhour");
	
	public RobotGang(){
		robotLimit = new HashMap<String, ApiLimit>();
		robotMap = new HashMap<String, List<Robot>>();
		oauthMap = new HashMap<String, OAuth>();
	}
	public void init(){
		try {
			readSer();
		} catch (Exception e) {
			// TODO: handle exception
			log.error("反序列化失败");
			e.printStackTrace();
		}
		for(String provider : Configuration.providers){
			List<Robot> readList = getRobots(provider, Robot.type_r);
			robotMap.put(provider + "-" + Robot.type_r, readList);
			
			List<Robot> writeList = getRobots(provider, Robot.type_w);
			robotMap.put(provider + "-" + Robot.type_w, writeList);
			
			List<Robot> rwList = getRobots(provider, Robot.type_rw);
			robotMap.put(provider + "-" + Robot.type_rw, rwList);
		}
	}
	
	private void readSer() throws Exception{
		String serPath = Configuration.getProperty("ser.path");
		File file = new File(serPath + "apiLimitMap");
		if(file.exists()){
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
			robotLimit = null;
			robotLimit = (Map<String, ApiLimit>)in.readObject();
			in.close();
		}
		if(robotLimit == null){
			robotLimit = new HashMap<String, ApiLimit>();
		}
		
	}
	
	public void destroy() throws Exception{
		String serPath = Configuration.getProperty("ser.path");
		File file = new File(serPath + "apiLimitMap");
		FileOutputStream outstream = new FileOutputStream(file);
		ObjectOutputStream out = new ObjectOutputStream(outstream);
		out.writeObject(robotLimit); //将这个对象写入流
		out.close();
	}
	
	public void addRobot(Robot robot){
		List<Robot> list = robotMap.get(robot.getProvider() + "-" + robot.getFlag());
		if(list == null){
			list = getRobots(robot.getProvider(), robot.getFlag());
			if(list == null){
				list = new ArrayList<Robot>();
			}
		}
		list.add(robot);
		robotMap.put(robot.getProvider() + "-" + robot.getFlag(), list);
		
	}
	
	public void save2DB(Robot robot){
		robotDao.save(robot);
	}
	
	public OAuth getOauth(String provider,Short type,Short rOrw){
		List<Robot> list = getRobots(provider, type);
		Robot robot = null;
		try {
			robot = getRobot(list, type,rOrw);
			if(robot == null)
				return null;
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			return null;
		}
		return new OAuth(provider, new AccessToken(robot.getAccessToken(),robot.getAccessSecret()));
	}
	
	public OAuth getOauth(String provider,Short type){
		return getOauth(provider,type,type);
	}
	
	public OAuth getOauth(String provider,Robot robot){
		OAuth oauth = oauthMap.get(robot.getId().toString());
		if(oauth == null){
			oauth = new OAuth(provider, new AccessToken(robot.getAccessToken(),robot.getAccessSecret()));
			oauthMap.put(robot.getId().toString(), oauth);
		}
		return oauth;
	}
	
	public List<Robot> getRobots(String provider,Short type){
		List<Robot> list = robotMap.get(provider + "-" + type);
		if(list == null){
			log.debug("get robots from db,provider=" + provider + ";type=" + type);
			String hql = "from Robot as r where r.flag=? and r.provider=?";
			list = robotDao.find(hql, type,provider);
			robotMap.put(provider + "-" + type, list);
			return list;
		}else{
			log.debug("get robots from cache");
			return list;
		}
	}
	
	public Robot getRobot(List<Robot> list ,Short type,Short rOrw) throws Exception{
		Robot robot = null;
		if(list == null || list.size() == 0){
			return null;
		}
		int i = 0;
		while(true){
			if(i == list.size()){
				log.debug("api limit");
				return null;
			}
			robot = list.get(i);
			log.debug(robot);
			String ymdhStr = Util.parseDate(Calendar.getInstance().getTime(), "yyyyMMddHH");
			Integer ymdh = Integer.valueOf(ymdhStr);
			ApiLimit limit = robotLimit.get(robot.getId().toString());
			if(limit == null){
				limit = new ApiLimit(0, 0, ymdh);
				robotLimit.put(robot.getId().toString(), limit);
			}
			log.debug(limit);
			if(rOrw.equals(Robot.type_w)){
				if(limit.getWriteCount() < limitWriteCount){
					limit.setWriteCount(limit.getWriteCount() + 1);
					log.info("use write api +1");
					break;
				}
			}else{
				if(limit.getReadCount() < limitReadCount){
					limit.setReadCount(limit.getReadCount() + 1);
					log.info("use read api +1");
					break;
				}
			}
			if(ymdh - limit.getYmdh() > 0){//新的一小时
				limit.setYmdh(ymdh);
				log.debug("new hour,recount api limit");
				if(rOrw.equals(Robot.type_w)){
					limit.setWriteCount(1);
					limit.setReadCount(0);
					log.info("use write api +1");
				}else{
					limit.setReadCount(1);
					limit.setWriteCount(0);
					log.info("use read api +1");
				}
				robotLimit.put(robot.getId().toString(), limit);
				break;
			}else{
				i++;
				log.info("this robot's api limited");
			}
		}
		return robot;
	}

	public Robot getRobot(List<Robot> list,Short type) throws Exception{
		return getRobot(list,type,type);
	}
	
	public static class ApiLimit implements Serializable{
		/**
		 * 
		 */
		private static final long serialVersionUID = -5077958563724556723L;
		private Integer ymdh;
		private Integer readCount;
		private Integer writeCount;
		public ApiLimit(Integer readCount,Integer writeCount,Integer ymdh) {
			this.ymdh = ymdh;
			this.readCount = readCount;
			this.writeCount = writeCount;
		}
		public Integer getYmdh() {
			return ymdh;
		}
		public void setYmdh(Integer ymdh) {
			this.ymdh = ymdh;
		}
		public Integer getReadCount() {
			return readCount;
		}
		public void setReadCount(Integer readCount) {
			this.readCount = readCount;
		}
		public Integer getWriteCount() {
			return writeCount;
		}
		public void setWriteCount(Integer writeCount) {
			this.writeCount = writeCount;
		}
		@Override
		public String toString(){
			return "ApiLimit{" +
			" ymdh=" + String.format("%1$11S", ymdh) +
			", readCount=" + String.format("%1$4S", readCount) +
	        ", writeCount=" + String.format("%1$4S", writeCount) +
	        '}';
		}
	}

	public IBaseDao<Robot, Integer> getRobotDao() {
		return robotDao;
	}

	public void setRobotDao(IBaseDao<Robot, Integer> robotDao) {
		this.robotDao = robotDao;
	}
}
